{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import backtrader as bt\n",
    "import datetime\n",
    "\n",
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 数据准备 300ETF 日级别数据\n",
    "dataframe = pd.DataFrame()\n",
    "for i in range(7):\n",
    "    df = pd.read_csv('../hist_data/510300_D_{}.csv'.format(2013+i), parse_dates=True, index_col=0)\n",
    "    dataframe = pd.concat([dataframe,df])\n",
    "dataframe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class SingleSMAStrategy(bt.Strategy):\n",
    "    params = (\n",
    "        ('maperiod', 15),\n",
    "    )\n",
    "    \n",
    "        \n",
    "    def __init__(self):\n",
    "        # Add a MovingAverageSimple indicator\n",
    "        self.sma = bt.indicators.SMA(\n",
    "             self.data0.close, period=self.p.maperiod)\n",
    "    \n",
    "        self.crossover = bt.indicators.CrossOver(self.data0.close, self.sma, plot=False)\n",
    "        # To keep track of pending orders\n",
    "        self.order = None\n",
    "        \n",
    "        \n",
    "    def notify_order(self, order):\n",
    "        if order.status in [order.Submitted, order.Accepted]:\n",
    "            # Buy/Sell order submitted/accepted to/by broker - Nothing to do\n",
    "            return\n",
    "\n",
    "        if order.status in [order.Completed, order.Canceled, order.Margin, order.Rejected]:\n",
    "            # Write down: no pending order\n",
    "            self.order = None\n",
    "\n",
    "    def next(self):\n",
    "        # Check if an order is pending ... if yes, we cannot send a 2nd one\n",
    "        if self.order:\n",
    "            return\n",
    "        \n",
    "        # Check if we are in the market\n",
    "        if not self.position:\n",
    "            # Not yet ... we MIGHT BUY if ...\n",
    "            if self.crossover[0] == 1:\n",
    "                # Keep track of the created order to avoid a 2nd order\n",
    "                self.order = self.buy()\n",
    "        else:\n",
    "            # Already in the market ... we might sell\n",
    "            if self.crossover[0] == -1:\n",
    "                # Keep track of the created order to avoid a 2nd order\n",
    "                self.order = self.sell()\n",
    "                \n",
    "    def stop(self):\n",
    "        print('(MA Period {:2d}) Ending Value {:.2f}'.format(\n",
    "            self.params.maperiod, self.broker.getvalue()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cerebro = bt.Cerebro()\n",
    "\n",
    "# pandasdata feeder\n",
    "df_feeder = bt.feeds.PandasData(dataname=dataframe, openinterest=None)\n",
    "\n",
    "cerebro.adddata(df_feeder, name= 'etf300')\n",
    "\n",
    "#cerebro.addstrategy(SingleSMAStrategy, maperiod=18)\n",
    "cerebro.optstrategy(SingleSMAStrategy, maperiod=range(15,91))\n",
    "\n",
    "\n",
    "# 小场面1万起始资金\n",
    "cerebro.broker.setcash(10000.0)\n",
    "\n",
    "# 手续费万5\n",
    "cerebro.broker.setcommission(0.0005)\n",
    "\n",
    "# 以发出信号当日收盘价成交\n",
    "cerebro.broker.set_coc(True)\n",
    "\n",
    "# Add a FixedSize sizer according to the stake\n",
    "cerebro.addsizer(bt.sizers.AllInSizerInt, percents=99)\n",
    "\n",
    "print('Starting Portfolio Value: {:.2f}'.format(cerebro.broker.getvalue()))\n",
    "\n",
    "cerebro.addanalyzer(bt.analyzers.AnnualReturn)\n",
    "cerebro.addanalyzer(bt.analyzers.TimeDrawDown)\n",
    "cerebro.addanalyzer(bt.analyzers.TradeAnalyzer)\n",
    "cerebro.addanalyzer(bt.analyzers.SQN)\n",
    "\n",
    "result = cerebro.run()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cols = ['sqn','trades','won_ratio','net','maxdd','maxddperiod']\n",
    "res_df = pd.DataFrame(columns=cols)\n",
    "annualreturn_df = pd.DataFrame(columns=range(2013,2020))\n",
    "\n",
    "for ret_list in result:\n",
    "    ret = ret_list[0]\n",
    "    l = list()\n",
    "    ana = ret.analyzers.sqn.get_analysis()\n",
    "    l.append(ana['sqn'])\n",
    "    l.append(ana['trades'])\n",
    "    \n",
    "    ana = ret.analyzers.tradeanalyzer.get_analysis()\n",
    "    won_ratio = 100*ana['won']['total']/ana['total']['closed']\n",
    "    l.append(won_ratio)\n",
    "    l.append(ana['pnl']['net']['total'])\n",
    "    \n",
    "    ana = ret.analyzers.timedrawdown.get_analysis()\n",
    "    l.append(ana['maxdrawdown'])\n",
    "    l.append(ana['maxdrawdownperiod'])\n",
    "\n",
    "    row = pd.Series(l, index=cols, name=ret.p.maperiod)\n",
    "    res_df = res_df.append(row)\n",
    "    \n",
    "    ana = ret.analyzers.annualreturn.get_analysis()\n",
    "    row = pd.Series(list(ana.values()), index=ana.keys(), name=ret.p.maperiod)\n",
    "    annualreturn_df = annualreturn_df.append(row)\n",
    "\n",
    "res_df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "\n",
    "_, axes = plt.subplots(3, 2, figsize=(10, 10), sharex=True)\n",
    "\n",
    "sns.set(style=\"darkgrid\")\n",
    "\n",
    "sns.lineplot(markers=True, data=res_df[['sqn']], ax=axes[0,0])\n",
    "sns.lineplot(markers=True, data=res_df[['trades']], ax=axes[0,1])\n",
    "sns.lineplot(markers=True, data=res_df[['won_ratio']], ax=axes[1,0])\n",
    "sns.lineplot(markers=True, data=res_df[['net']], ax=axes[1,1])\n",
    "sns.lineplot(markers=True, data=res_df[['maxdd']], ax=axes[2,0])\n",
    "sns.lineplot(markers=True, data=res_df[['maxddperiod']], ax=axes[2,1])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.heatmap(annualreturn_df, annot=True, fmt=\".3f\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cerebro = bt.Cerebro()\n",
    "\n",
    "# pandasdata feeder\n",
    "df_feeder = bt.feeds.PandasData(dataname=dataframe, openinterest=None)\n",
    "\n",
    "cerebro.adddata(df_feeder, name= 'etf300')\n",
    "cerebro.addstrategy(SingleSMAStrategy, maperiod=79)\n",
    "\n",
    "# 小场面1万起始资金\n",
    "cerebro.broker.setcash(10000.0)\n",
    "\n",
    "# 手续费万5\n",
    "cerebro.broker.setcommission(0.0005)\n",
    "\n",
    "# 以发出信号当日收盘价成交\n",
    "cerebro.broker.set_coc(True)\n",
    "\n",
    "# Add a FixedSize sizer according to the stake\n",
    "cerebro.addsizer(bt.sizers.AllInSizerInt, percents=99)\n",
    "\n",
    "cerebro.run()\n",
    "cerebro.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
